﻿/* Author: Tomer Paz
   games.com
   2007
*/
// the game Commands concrete class - same as CMD class, but for Local-Server connection (Tests/Demo only)
// implements the network commands Sent and Recieved to/from game server
// encapsulates (composite) a LocalConnection (!) object to connect to server.
// A Singleton.
// Note: Server responses are asynchronic since Flash LocalConnection
// implementation is asynchronic.
// hence, the each function must look in the private "Receipt" storage parameter for the server respond, right after
// sending the command to server. there is no other way to implement this pseudo-synchronic operation. with Sockets
// this issue would not exist.

package monopoly.CMD {

	import monopoly.CMD.ICMD;
	import flash.net.LocalConnection;
	import monopoly.IFS.utils.Sleep;
	import monopoly.Player.Player;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.ByteArray;

	public class LocalCMD extends EventDispatcher implements ICMD
		{
		// constant Protocol commands identification
		public static const CONNECT:uint		= 100;
		public static const INTRO:uint  		= 120;
		public static const PLAYER_TYPE:uint 	= 160;
		public static const STATUS:uint 		= 200;
		public static const THROW_DIES:uint 	= 400;
		public static const DONE_MOVE:uint  	= 420;
		public static const ACTION_NONE:uint	= 430;
		public static const ACTION_PAY:uint 	= 440;
		public static const ACTION_BUY:uint 	= 450;
		public static const ACTION_SELL:uint	= 460;
		public static const ACTION_MORTGAGE:uint= 470;
		public static const ACTION_ENDTURN:uint = 555
		public static const DISCONNECT:uint 	= 999;
		
		// a singleton
		private static var _instance:LocalCMD;
		
		private var _Sender:LocalConnection;
		private var _Channel:String;
		private var _ChannelReceipt:String;
		private var _Receipt:Array = [ 0, null ];	// this array shall be used for the Receipt from the game Server
		private var _Status:Array;
		private var _CurrentState:uint = 0;
		private var _ClientID:uint;
		private var _SessionID:String;
		private var _ChallangeRes:ByteArray;
		
		public function LocalCMD(parameter:SingletonEnforcer):void
			{
			}
			
		// Singleton implementation	- the Port here is really just pseudo not to break ICMD interface...
		public static function getInstance(ChannelName:String, Port:int):LocalCMD
			{
	        if(_instance == null) 
				{
	            	_instance = new LocalCMD(new SingletonEnforcer());
					_instance._Sender  = new LocalConnection( );
					_instance._Channel = ChannelName;
					_instance._ChannelReceipt = ChannelName + "Receipt";
					// to be used be serer set_status broadcast command
					_instance._Status = new Array();
	         	}
	        return LocalCMD._instance;
	      	}
		
		/* ICMD interface implementation */
		
		public function get State():int { return _instance._CurrentState; }
		public function set State(state:int):void { _instance._CurrentState = state; }
		
		public function set ClientID(clientid:uint):void
			{
			_instance._ClientID = uint(clientid);
			}
		public function set SessionID(sessionID:String):void
			{
			_instance._SessionID = sessionID;
			}
			
		// cmd code = 100 - returns success/fail for establishing connection to LOCAL g.server
		// connects using LocalConnection (!) which was initiated during class instance creation
		public function readSRVHLO(cmdRes:CMDResponse):void
			{
			try {
				// Sender only needs to 'connect' the Server-Respond channel = ChannelReceipt
				_instance._Sender.connect(_ChannelReceipt);
				_instance._Sender.client = _instance;
				}
			catch (error:Error)
				{
				trace("error: already connected");
				}
			}
		public function readACCEPT(cmdRes:CMDResponse):void {}
		public function get Authorized():Boolean { return true; }
		
		// take care of receipt- Handler
		public function receipt(CODE:Number, ReturnData:Object):void
			{
			// fill in our local storage
			_instance._Receipt[0] = CODE;
			_instance._Receipt[1] = ReturnData;
			}
		private function reset_receipt():void
			{
			_instance._Receipt[0] = 0;
			_instance._Receipt[1] = null;
			}
		
		// cmd code = 120 - returns Allow/Deny (= true/false)
		public function sendINTRO(clientVer:int, gameID:int):void
			{
			// for demo - send strings rather than ByteArray:
			//_instance._Sender.send(_Channel, "introduce", ByteArray(cmdRes.Content));

//			return(cmd_result_boolean("introduce", INTRO));
			}
		// cmd code = 160	- sends ready command with Player requested state (Player type 0 - viewer, 1 - player)
		// server shall return verification receipt before client can proceed to get_status command
		public function ready(PlayerType:int):void
			{
			// server needs to know client preferred state - player or viewer
			_instance._Sender.send(_Channel, "ready", PlayerType);

//			return(cmd_result_boolean("ready", PLAYER_TYPE));
			}
		public function sendStart():void { }

			
		/* add chat system */
		public function chat(message:String, player_id:uint, rcpt:uint):void
			{
			}
		// cmd code = 200 - fills referenced parmeters.
		// Returns Mode - wait X seconds or Move (0 for move, >0 how many seconds to wait)
		// this function in client actually Listens to Server broadcast and responds to Server "set_status"
		// command.
		public function set_status(Status:Array):void
			{
			// update our instance to be accessed by "get" function by the clients (=GameController)
			// Warning! used concat and not duplicate - this will Not work in case of recursive/referenced array!
			// assuming simple 'flat' int array
			_instance._Status = Status.concat( );
			
			// dispatch event so whoever waits for this update will get attention
			dispatchEvent(new Event(Event.CHANGE));
			}
		public function get Status():Array { return _instance._Status.concat( ); }
		
		/* now the game commands */
		
		// sends the dies request - 400
		public function throw_dies():void
			{
			_instance._Sender.send(_Channel, "throw_dies");	
			/*
			var answer:uint = 0;
			
			Sleep(500);
			
			if (_instance._Receipt[0] == THROW_DIES)
				{
				answer = uint(_instance._Receipt[1]);
				
				// reset receipt
				reset_receipt();
				}
				
			return (answer);
			*/
			}
		// client returns desired move and expect server approval (allow/deny = true/false) - 420
		public function done_move(NewPosition:uint):void
			{
			_instance._Sender.send(_Channel, "done_move", NewPosition);
			
//			return(cmd_result_boolean("done_move", DONE_MOVE));
			}
		// client disconnect session - 999
		public function CMDdisconnect():void
			{
			_instance._Sender.send(_Channel, "CMDdisconnect");
			
			_instance._Sender.close( );
			}
		public function ac_resign():void
			{
			_instance._Sender.send(_Channel, "Resign");
			_instance._Sender.close( );
			}
			
		/* property actions */
	
		// action none - 430
		public function ac_none():void
			{
			_instance._Sender.send(_Channel, "ac_none");

//			return(cmd_result_boolean("ac_none", ACTION_ENDTURN));
			}
		// action pay  - 440 - pay to owner - "Player" is the index in Players container which is stored in the
		// same manner in the game server !!
		public function ac_pay(Player:uint, PropertyID:uint, Sum:uint):void
			{
			_instance._Sender.send(_Channel, "ac_pay", Player, Sum);
			
//			return(cmd_result_boolean("ac_pay", ACTION_PAY));
			}
		// action buy  - 450
		public function ac_buy(Player:uint, PropertyID:uint, Sum:uint):void
			{
			_instance._Sender.send(_Channel, "ac_buy", Player, PropertyID, Sum);
			
//			return(cmd_result_boolean("ac_buy", ACTION_BUY));
			}
		// action sell - 460
		public function ac_sell(Player:uint, PropertyID:uint, Sum:uint):void
			{
			_instance._Sender.send(_Channel, "ac_sell", Player, PropertyID, Sum);
			
//			return(cmd_result_boolean("ac_sell", ACTION_SELL));
			}
		public function ac_mortgage(Player:int, Properties:Array, Sum:int):void
			{
			_instance._Sender.send(_Channel, "ac_mortgage", Player, Properties, Sum);
			
//			return(cmd_result_boolean("ac_mortgage", ACTION_MORTGAGE));
			}
		public function ac_upgrade(fieldID:int, upgrade_level:int):void
			{
			_instance._Sender.send(_Channel, "ac_upgrade", fieldID, upgrade_level);
			}
		public function ac_auction(fieldID:int, sum:int):void
			{
			_instance._Sender.send(_Channel, "ac_auction", fieldID, sum);
			}
		// just inform the server the turn is over - retuns success/fail- 555
		public function end_turn():void
			{
			_instance._Sender.send(_Channel, "end_turn");
			
//			return(cmd_result_boolean("end_turn", ACTION_ENDTURN));			
			}
			
		// lets make a 'template' like action Result function since it is a pain in the neck to copy the same
		// functioncality over and over...why aren't there Templates in AS ?! or at least MACRO's ?
		private function cmd_result_boolean(RPC_name:String, RET_CODE:uint)
			{
			// sleep a bit to let server answer - ugly but should work for demo
			Sleep(500);
			
			var answer:Boolean = false;
			if (_instance._Receipt[0] == RET_CODE)
				{
				answer = Boolean(_instance._Receipt[1]);
				
				// trace server answer (OK/Denied)
				trace("Server respond to" + RPC_name + " command was: " + answer);
				
				// reset receipt
				reset_receipt();
				}
				
			return answer;
			}
			
		/* some getters */

		public function get SessionID():String { return _instance._SessionID; }
		public function get ServerName():uint { return 1; }
		public function get ServerVersion():uint { return 1; }
		
		public function readSTATUS(cmdRes:CMDResponse):void { }
		
		public function ac_req_buy(response:uint):void { }								// in response to other player buy request
		public function ac_req_exchange(response:uint):void { }							// in response to other player exchange request
		public function ac_req_partner(response:uint):void  { }							// in response to other player partner request
		public function ac_req_sell(response:uint):void { }								// in response to other player sell request
		}
}
class SingletonEnforcer {}
